Skip to content

Conversation

@gedare
Copy link
Contributor

@gedare gedare commented Jan 27, 2025

An attribute in a class/struct declaration confuses the annotator to treat the identifier following the attribute as a function or variable name.

Fixes #124574

@llvmbot
Copy link
Member

llvmbot commented Jan 27, 2025

@llvm/pr-subscribers-clang-format

Author: Gedare Bloom (gedare)

Changes

An attribute in a class/struct declaration confuses the parser to treat the identifier following the attribute as a function or variable name.

Fixes #124574


Full diff: https://github.com/llvm/llvm-project/pull/124634.diff

2 Files Affected:

  • (modified) clang/lib/Format/TokenAnnotator.cpp (+11-3)
  • (modified) clang/unittests/Format/FormatTest.cpp (+12)
diff --git a/clang/lib/Format/TokenAnnotator.cpp b/clang/lib/Format/TokenAnnotator.cpp
index 08fda7a2ffa51a..3b4b0b78e8bf16 100644
--- a/clang/lib/Format/TokenAnnotator.cpp
+++ b/clang/lib/Format/TokenAnnotator.cpp
@@ -2633,12 +2633,20 @@ class AnnotatingParser {
              PreviousNotConst->MatchingParen->Previous->isNot(tok::kw_template);
     }
 
-    if ((PreviousNotConst->is(tok::r_paren) &&
-         PreviousNotConst->is(TT_TypeDeclarationParen)) ||
-        PreviousNotConst->is(TT_AttributeRParen)) {
+    if (PreviousNotConst->is(tok::r_paren) &&
+        PreviousNotConst->is(TT_TypeDeclarationParen)) {
       return true;
     }
 
+    auto InTypeDecl = [&]() {
+      for (auto Next = Tok.Next; Next; Next = Next->Next)
+        if (Next->isOneOf(TT_ClassLBrace, TT_StructLBrace))
+          return true;
+      return false;
+    };
+    if (PreviousNotConst->is(TT_AttributeRParen) && (!IsCpp || !InTypeDecl()))
+      return true;
+
     // If is a preprocess keyword like #define.
     if (IsPPKeyword)
       return false;
diff --git a/clang/unittests/Format/FormatTest.cpp b/clang/unittests/Format/FormatTest.cpp
index 265461561d2012..6145500a56582a 100644
--- a/clang/unittests/Format/FormatTest.cpp
+++ b/clang/unittests/Format/FormatTest.cpp
@@ -12415,6 +12415,18 @@ TEST_F(FormatTest, UnderstandsAttributes) {
   verifyFormat("SomeType s __unused{InitValue};", CustomAttrs);
   verifyFormat("SomeType *__capability s(InitValue);", CustomAttrs);
   verifyFormat("SomeType *__capability s{InitValue};", CustomAttrs);
+
+  FormatStyle Style = getLLVMStyle(FormatStyle::LK_Cpp);
+  verifyFormat(
+      "template <>\n"
+      "struct __declspec(uuid(\"3895C200-8F26-4F5A-B29D-2B5D72E68F99\"))\n"
+      "IAsyncOperation<IUnknown *> : IAsyncOperation_impl<IUnknown *> {};",
+      Style);
+  verifyFormat(
+      "template <>\n"
+      "class __declspec(uuid(\"3895C200-8F26-4F5A-B29D-2B5D72E68F99\"))\n"
+      "IAsyncOperation<IUnknown *> : IAsyncOperation_impl<IUnknown *> {};",
+      Style);
 }
 
 TEST_F(FormatTest, UnderstandsPointerQualifiersInCast) {

An attribute in a class/struct declaration confuses the annotater to treat
the identifier following the attribute as a function or variable name.

Fixes llvm#124574
@gedare gedare changed the title [clang-format] Fix parsing attrs in class/struct [clang-format] Fix annotating attrs in class/struct Jan 28, 2025
Copy link
Contributor

@HazardyKnusperkeks HazardyKnusperkeks left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'd add an annotating test. (And drop the formatting test.)

@nico
Copy link
Contributor

nico commented Jan 28, 2025

(Why drop the formatting test? Seems good to have an integration test for this, no?)

@HazardyKnusperkeks
Copy link
Contributor

(Why drop the formatting test? Seems good to have an integration test for this, no?)

Because the bug was a misannotation. The formatting, based on the annotations, should already be covered. Of course one can always add more tests, and I'm not demanding to remove it, I simply suggest it. The more test cases there are, the longer the duration of building and running the tests for all (clang-format) developers and CIs.

@gedare
Copy link
Contributor Author

gedare commented Jan 28, 2025

I'd add an annotating test. (And drop the formatting test.)

Yes, that's a good idea, I've done it. I started with the formatting test, but it is unnecessary (although a good, complicated bit of formatting) since the bug was in the annotation.

}

auto InTypeDecl = [&]() {
for (auto Next = Tok.Next; Next; Next = Next->Next)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

A more robust and efficient way would be to annotate the class name in UnwrappedLineParser as most of the work has already been done there. I'll submit a patch soon.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

See #124891.

@gedare gedare closed this Jan 29, 2025
@gedare gedare reopened this Jan 29, 2025
@gedare
Copy link
Contributor Author

gedare commented Jan 29, 2025

The improved fix is in #124891.

@gedare gedare closed this Jan 29, 2025
@gedare gedare deleted the 124574-declspec branch January 29, 2025 17:21
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Regression clang-formatting __declspec(uuid(...))

5 participants